package com.eu.mybatis.helper;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.eu.mybatis.config.InterceptorConfig;

public class InterceptorHelper {
	
	private static Logger logger = LoggerFactory.getLogger(InterceptorHelper.class);
	
	protected static final Map<String, String> MAP = new HashMap<String, String>();
	
	static {
		MAP.put("mappedStatement", "delegate.mappedStatement");
		MAP.put("configuration", "delegate.configuration");
		MAP.put("rowBounds", "delegate.rowBounds");
		MAP.put("boundSql", "delegate.boundSql");
		MAP.put("boundSql.sql", "delegate.boundSql.sql");
		MAP.put("rowBounds.limit", "delegate.rowBounds.limit");
		MAP.put("rowBounds.offset", "delegate.rowBounds.offset");
	}
	
	private static String key(boolean delegate, String key) {
		return delegate ? (String) MAP.get(key) : key;
	}

	public static StatementHandler getStatementHandler(Invocation invocation) {
		return (StatementHandler) invocation.getTarget();
	}

	/**
	 * 从metaObject获取【key】对应的值
	 * 
	 * @param metaObject 是Mybatis提供的一个的工具类
	 * 通过它包装一个对象后可以获取或设置该对象的原本不可访问的属性（比如那些私有属性）
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getValue(MetaObject metaObject, boolean delegate, String key) {
		return (T) metaObject.getValue(key(delegate, key));
	}

	/**
	 * metaObject设置【key】对应的【value】
	 * 通过OGNL方式为metaObject注入
	 */
	public static void setValue(MetaObject metaObject, boolean delegate, String key, Object value) {
		metaObject.setValue(key(delegate, key), value);
	}

	public static String formatSql(String sql) {
		return sql.replaceAll("[\r\n]", " ").replaceAll("\\s{2,}", " ");
	}

	public static BoundSql getBoundSql(boolean delegate, MetaObject metaObject) {
		return (BoundSql) getValue(metaObject, delegate, "boundSql");
	}

	public static boolean isDelegate(StatementHandler statementHandler) {
		return statementHandler instanceof RoutingStatementHandler;
	}
	
	public static void setTotalRows(Connection connection, String countSql, Object parameterObject, BoundSql countBoundSql, MappedStatement ms) throws Exception {
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			DefaultParameterHandler handler = new DefaultParameterHandler(ms, parameterObject, countBoundSql);
			pstmt = connection.prepareStatement(countSql);
			handler.setParameters(pstmt);
			rs = pstmt.executeQuery();
			int count = 0;
			if (rs.next()) {
				count = rs.getInt(1);
			}
			RowsHelper.setTotalRows(count);
			
			if(logger.isDebugEnabled()) {
				logger.debug("Total count ==> {}, sql ==> {}, params ==> {}", Integer.valueOf(count), countSql,  parameterObject);
			}
		} catch (Exception e) {
			RowsHelper.setTotalRows(0);
			throw e;
		} finally {
			try {
				if(rs != null) {
					rs.close();
				}
				if(pstmt != null) {
					pstmt.close();
				}
			} catch (Exception e2) {
			}
		}
	}
	
	/**
	 * 记录sql日志
	 */
	public static void logExecuteSql(InterceptorConfig interceptorConfig) {
		String sqlLower = interceptorConfig.getSql().trim().toLowerCase();
		if ((sqlLower.contains("drop ")) || (sqlLower.contains("truncate "))) {
			throw new IllegalArgumentException("please confirm, drop/truncate ? the sql is ==>" + sqlLower);
		}
		
		if (sqlLower.startsWith("select")) {
			if(logger.isDebugEnabled()) {
				logger.debug("query sql ==> {}, parameters ==> {}", sqlLower, formatParams(interceptorConfig));
			}
		} else {
			logger.debug("sql without query ==> {}, parameters ==> {}", sqlLower, formatParams(interceptorConfig));
		}
	}

	/**
	 * 格式化参数，如果参数是collection或map类型，并且超过10个。则不打印全部内容
	 */
	public static Object formatParams(InterceptorConfig processInfo) {
		Object params = processInfo.getParameterObject();
		int size = 0;
		if ((((params instanceof Collection)) && ((size = ((Collection<?>) params).size()) > 10)) || (((params instanceof Map)) && ((size = ((Map<?, ?>) params).size()) > 10))) {
			params = params.getClass().getName() + " Size ==> " + size;
		}
		return params;
	}
	
}